package io.chenhd.gateway.outbound.okhttp;

import io.chenhd.gateway.config.RouterConfig;
import io.chenhd.gateway.router.HttpEndpointRouter;
import io.chenhd.gateway.threadpool.ThreadPool;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpUtil;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.util.List;
import java.util.concurrent.TimeUnit;

import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;

public class OkhttpHandler extends ChannelInboundHandlerAdapter {

    private List<String> backendUrls;
    private HttpEndpointRouter router;

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        FullHttpRequest fullRequest = (FullHttpRequest) msg;
        String requestUri = fullRequest.uri();
        String backendUrl = RouterConfig.getBackendUrl(requestUri);
        if (backendUrl == null) {
            handleResponseNotFound(ctx);
        }
        final String url = backendUrl + fullRequest.uri();
        try {
            ThreadPool.threadPoolExecutor.submit(() -> {
                try {
                    Response response = httpGet(url);
                    handleResponse(fullRequest, ctx, response);
                } catch (Exception e) {
                    e.printStackTrace();
                    //log error
                }
            });
        } catch (Exception e) {
            //log error
            System.out.println("thread pool full!");
        }
    }

    private Response httpGet(String url) {
        OkHttpClient client = new OkHttpClient.Builder().build();
        Response response = null;
        int retry = 0;
        while (retry++ <= 3) {
            try {
                Request request = new Request.Builder().url(url).build();
                response = client.newCall(request).execute();
                if (response.code() == 200) {
                    System.out.println("okhttp request success to " + url);
                    return response;
                }
                TimeUnit.SECONDS.sleep(2);
            } catch (Exception e) {
                e.printStackTrace();
                //error log
            }
        }
        return response;
    }

    private void handleResponse(final FullHttpRequest fullRequest, final ChannelHandlerContext ctx, final Response endpointResponse) throws Exception {
        FullHttpResponse response = null;
        try {
            byte[] body = endpointResponse.body().bytes();

            response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(body));

            response.headers().set("Content-Type", "application/json");
            response.headers().setInt("Content-Length", Integer.parseInt(endpointResponse.header("Content-Length")));


        } catch (Exception e) {
            e.printStackTrace();
            response = new DefaultFullHttpResponse(HTTP_1_1, NO_CONTENT);
            exceptionCaught(ctx, e);
        } finally {
            if (fullRequest != null) {
                if (!HttpUtil.isKeepAlive(fullRequest)) {
                    ctx.write(response).addListener(ChannelFutureListener.CLOSE);
                } else {
                    //response.headers().set(CONNECTION, KEEP_ALIVE);
                    ctx.write(response);
                }
            }
            ctx.flush();
            //ctx.close();
        }
    }

    private void handleResponseNotFound(final ChannelHandlerContext ctx) throws Exception {
        String content = "url error";
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, NOT_FOUND, Unpooled.wrappedBuffer(content.getBytes()));
        response.headers().set("Content-Type", "application/json");
        response.headers().setInt("Content-Length", content.length());
        ctx.write(response);
        ctx.flush();
//        ctx.close();
    }
}
